package com.spring.sd;

import com.spring.sd.bfpp.AtBeanPostProcessor;
import com.spring.sd.bfpp.ComponentScanPostProcessor;
import com.spring.sd.bfpp.Config;
import com.spring.sd.bfpp.MapperPostProcessor;
import com.spring.sd.bfpp.component.Bean2;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.context.annotation.ConfigurationClassPostProcessor;
import org.springframework.context.support.GenericApplicationContext;

/**
 * 常见的 BeanFactory 后置处理器
 */
public class BeanFactoryPostProcessorApp {

    public static void main(String[] args) {
        //step1();
        //step2();
        //defineComponentScanPostProcessor();
        //step3();
        //mapperResolve();
        step4();
    }


    /**
     * @ComponentScan @Bean @Import @ImportResource
     */
    public static void step1() {
        // 继续使用 GenericApplicationContext 作为容器，向容器中注册 config
        GenericApplicationContext context = new GenericApplicationContext();
        context.registerBean("config", Config.class);

        // @ComponentScan @Bean @Import @ImportResource
        context.registerBean(ConfigurationClassPostProcessor.class);

        context.refresh();

        for (String name : context.getBeanDefinitionNames()) {
            System.out.println(">>>>>>>>>>>> " + name);
        }

        context.close();
    }

    /**
     * @Mapper
     */
    public static void step2() {
        GenericApplicationContext context = new GenericApplicationContext();
        context.registerBean("config", Config.class);
        // @ComponentScan @Bean @Import @ImportResource
        context.registerBean(ConfigurationClassPostProcessor.class);

        // @Mapper注解
        // 其中的 basePackage 是 MapperScannerConfigurer 中的一个成员变量，表示需要扫描的包路径，
        // 设置的值恰好是被 @Mapper 注解标记的接口所在的包路径。
        context.registerBean(MapperScannerConfigurer.class,
                i -> i.getPropertyValues().add("basePackage", "com.spring.sd.bfpp.mapper"));

        context.refresh();

        for (String name : context.getBeanDefinitionNames()) {
            System.out.println(">>>>>>>>>>>> " + name);
        }

        context.close();

    }


    /**
     * 模拟实现@ComponentScan
     * 移除向容器中添加的 ConfigurationClassPostProcessor 和 MapperScannerConfigurer 两个后置处理器，自行编码模拟它们功能的实现。
     *
     * 组件扫描之 @ComponentScan
     *
     * 1、在 com.spring.sd.bfpp.component.Bean2 所在包路径下再增加两个类，用于后续测试。
     * 2、
     */
    public static void defineComponentScanPostProcessor() {
        GenericApplicationContext context = new GenericApplicationContext();
        context.registerBean("config", Config.class);
        // 没使用 ConfigurationClassPostProcessor 也实现了 @ComponentScan 注解的解析！
        context.registerBean(ComponentScanPostProcessor.class);

        context.refresh();

        for (String name : context.getBeanDefinitionNames()) {
            System.out.println(name);
        }

        context.close();
    }

    /**
     * 解析 @Bean 注解
     */
    public static void step3() {
        GenericApplicationContext context = new GenericApplicationContext();
        context.registerBean("config", Config.class);
        context.registerBean(AtBeanPostProcessor.class);

        context.refresh();

        for (String name : context.getBeanDefinitionNames()) {
            System.out.println(name);
        }

        context.close();

    }


    /**
     * @Mapper 的解析
     */
    public static void mapperResolve() {

        /**
         * @Mapper 注解是在接口上使用的，但根据前文内容可知，@Mapper 被解析后在 Spring 容器中也存在与被标记的接口相关的 Bean。
         *
         * 难道 Spring 能管理接口？
         * 那肯定是不行的，Spring 只能管理对象这是毋庸置疑的。那这些接口是怎么变成对象被 Spring 管理的呢？
         *
         * 这依赖于 MapperFactoryBean 将接口转换为对象。
         * 在 Config 添加注册 Mapper1 和 Mapper2 的方法
         *
         * 这种方式虽然可以完成 Mapper 接口的注册，但每次只能单个注册，不能批量注册。
         */

        GenericApplicationContext context = new GenericApplicationContext();
        context.registerBean("config", Config.class);
        // @ComponentScan @Bean @Import @ImportResource
        context.registerBean(ConfigurationClassPostProcessor.class);


        context.refresh();

        for (String name : context.getBeanDefinitionNames()) {
            System.out.println(">>>>>>>>>>>> " + name);
        }

        context.close();
    }

    /**
     * @Mapper 注解的解析
     */
    public static void step4() {
        GenericApplicationContext context = new GenericApplicationContext();
        context.registerBean("config", Config.class);

        context.registerBean(AtBeanPostProcessor.class);
        /*
         * AtBeanPostProcessor 的注册不能少，因为需要容器中存在 SqlSessionFactoryBean
         * 而 SqlSessionFactoryBean 是在配置类中利用 @Bean 进行注册的
         */
        context.registerBean(MapperPostProcessor.class);

        context.refresh();

        for (String name : context.getBeanDefinitionNames()) {
            System.out.println(">>>>>>>>>>>> " + name);
        }

        context.close();
    }
}
